package momo.multitree.simulation;

import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import momo.multitree.structure.Graph;
import momo.multitree.structure.Node;
import momo.multitree.structure.Tree;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class NoiseTestFrameWork {
	
	private Log log = LogFactory.getLog(NoiseTestFrameWork.class);
	
	final private long seed = 534983419;
	private Random rand = new Random(seed);
	
	private int sizes[] = {5, 6, 7, 8, 9, 10, 12, 15, 20, 25, 30, 40, 50};
	private String[] filenames;
	
	final private int maxIteration = 100;
	final private int maxTrial = 100;
	final private double temperature = 0d;
	final private double factor = 0.98d;
	
	final private double mean = 0d;
	final private double stdev = 0.2d;
	
	public NoiseTestFrameWork() {
		filenames = new String[ sizes.length*100 ];
		int counter = 0;
		for(int i=0; i<sizes.length; i++)
		{
			for(int j=0; j<100; j++) {
				filenames[ j + (counter*100) ] = sizes[i]+"_node_symmetric_dataset_" + (j+1) +".txt";
			}
			counter ++;
		}	
	}
	
	public void run() {
		for(String filename: filenames)
		{
			try
			{
				Map<String, Double> map = new HashMap<String, Double>();
				
				Graph orgGraph = new Graph(new FileReader("dataset/"+filename));
				Graph noiseGraph = new Graph(new FileReader("dataset/"+filename));
				
				setMapping(map, orgGraph);
				setNoise(noiseGraph);
				
				SimulatedAnnealing sim = new SimulatedAnnealing(orgGraph, maxIteration, maxTrial, temperature, factor);
				Tree orgBestTree = sim.optimizeTree();
				
				sim = new SimulatedAnnealing(noiseGraph, maxIteration, maxTrial, temperature, factor);
				Tree noiseBestTree = sim.optimizeTree();
				resetMapping(map, noiseBestTree);
				
				log.info(filename+","+displayInfo( orgBestTree, noiseBestTree ) );
				
			}catch(Exception e)
			{
				log.error("Error on filename: " + filename);
				e.printStackTrace();
			}
		}
	}
	
	public String displayInfo(Tree org, Tree noise) {
		double orgStab = org.compStability();
		double noiseStab = noise.compStability();
		double scaled_diff = ( noiseStab - orgStab )/orgStab;
		return  orgStab + "," +
				noiseStab + "," +
				scaled_diff;
	}
	
	public void setMapping(Map<String, Double> map, Graph g) {
		for( Node n : g.getNodes() ) {
			map.put( n.getId() , n.getStability());
		}
	}
	
	public void resetMapping(Map<String, Double> map, Graph g) {
		for( Node n : g.getNodes() ) {
			n.setStability( map.get(n.getId()) );
		}
	}
	
	public void setNoise(Graph g) {
		for ( Node n : g.getNodes() ) {
			n.setStability( Math.abs( n.getStability() + getGaussian(mean, stdev) ) );
		}
	}
	
	/**
	 * java.util.Random.nextGaussian - produces numbers with mean 0.0 and standard deviation 1.0.
	 * This method allows maniplulation over the mean and standard deviation by simple formula
	 * n′ = ( n * σ ) + μ;
	 * where n = output from Random.nextGaussian, σ = desired standard deviation and μ = desired mean
	 * @return
	 */
	public double getGaussian(double mean, double stdev) {
		return ( rand.nextGaussian() * stdev ) + mean;
	}
	
	public void testGetGaussian() {
		double mean = 0;
		double stdev = 0.2;
		double max = Math.abs( getGaussian(mean, stdev) );
		for(int i=0; i<100; i++) {
			max = Math.max(max, Math.abs( getGaussian(mean, stdev) ) );
		}
		System.out.println(max);
	}
	
	public static void main(String args[]) {
		NoiseTestFrameWork app = new NoiseTestFrameWork();
		app.run();
//		app.testGetGaussian();
	}
	
}//end of class NoiseTestFrameWork
